package com.uni.crawl.common.util.crawl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.json.XML;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import javax.xml.transform.*;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.ByteArrayInputStream;
import java.io.StringReader;
import java.io.StringWriter;

/**
 * 抽取工具
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ExtractUtils {

    /**
     * 通过XSLT生成标准的XML需要有根节点，此项目默认了详情页根节点名称
     */
    public static final String XML_DEF_ROOT = "root";

    private static final String XSL_TRANSFORM = "xsl:transform";
    private static final String XSL_STYLESHEET = "xsl:stylesheet";

    private static final String XSLT_HEAD = """
            <?xml version="1.0" encoding="UTF-8"?>
            <xsl:transform version="3.0"
              xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
              xmlns:java="com.uni.crawl.common.util.crawl" exclude-result-prefixes="java">
            """;

    private static final String XSLT_TAIL = "</xsl:transform>";

    /**
     * XSLT模板。
     * 一般就是从界面上配置的模板
     *
     * @param xsltTemplate XSLT模板
     */
    public static Transformer getTransformerByString(String xsltTemplate) throws TransformerConfigurationException {
        if (!CharSequenceUtil.containsIgnoreCase(xsltTemplate, XSL_TRANSFORM) && !CharSequenceUtil.containsIgnoreCase(xsltTemplate, XSL_STYLESHEET)) {
            // 界面上省略了xslt模板的头和尾部，简化配置复杂度
            xsltTemplate = XSLT_HEAD + "\n" + xsltTemplate + "\n" + XSLT_TAIL;
        }
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Templates template = transformerFactory.newTemplates(new StreamSource(new ByteArrayInputStream(xsltTemplate.getBytes())));
        return template.newTransformer();
    }

    /**
     * XSLT模板文件路径。
     * 从磁盘路径加载的XSLT模板，一般用于单元测试
     *
     * @param xsltFile XSLT模板文件路径
     */
    public static Transformer getTransformerByFile(String xsltFile) throws TransformerConfigurationException {
        return getTransformerByString(FileUtil.readUtf8String(xsltFile));
    }

    public static Source getSourceByString(String xml) {
        if (JSONUtil.isTypeJSON(xml)) {
            xml = JSONUtil.toXmlStr(JSONUtil.parseObj(xml));
        }
        String xhtml = formatHtml(xml);
        StringReader reader = new StringReader(xhtml);
        return new StreamSource(reader);
    }

    public static Source getSourceByFile(String xmlFile) {
        return getSourceByString(FileUtil.readUtf8String(xmlFile));
    }

    public static JSONObject getJSONObject(Transformer transformer, Source source) throws TransformerException {
        JSONObject ret = doGetJSONObject(transformer, source);
        JSONObject finalRet = ret.getJSONObject(XML_DEF_ROOT);
        finalRet.remove("xmlns");
        return finalRet;
    }

    public static JSONArray getJSONArray(Transformer transformer, Source source) throws TransformerException {
        JSONObject ret = doGetJSONObject(transformer, source);
        return ret.getJSONArray(XML_DEF_ROOT);
    }

    private static JSONObject doGetJSONObject(Transformer transformer, Source source) throws TransformerException {
        StringWriter stringWriter = new StringWriter();
        Result result = new StreamResult(stringWriter);
        transformer.transform(source, result);
        return XML.toJSONObject(stringWriter.toString());
    }

    /**
     * 格式化html
     *
     * @param html 原始html，带有未关闭的标签
     */
    private static String formatHtml(String html) {
        Document doc = Jsoup.parse(html);
        doc.outputSettings().syntax(Document.OutputSettings.Syntax.xml);
        return doc.toString();
    }
}
